﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;


namespace Tensop.Net.Core
{
    public class IOCPBase : ISocketAsyncEventHandler
    {
        MemoryManager alloc;
        
        public IOCPBase(int max)
        {
            alloc = new MemoryManager( max );
            alloc.InitBuffer();
        }

        public event Action<Socket> _OnAccept;
        public void Accept(SocketAsyncEventArgs e)
        {
            if ( e.AcceptSocket != null && e.AcceptSocket.Connected )
            {
                _OnAccept?.Invoke( e.AcceptSocket );
            }
        }


        public event Action<ConnectionContext> _OnDisconnect;
        public void Disconnect(SocketAsyncEventArgs e)
        {
            if ( e.UserToken is ConnectionContext cc )
                _OnDisconnect?.Invoke( cc );
        }

        public event Action<ConnectionContext,byte[]> _OnReceive;        
        public void Receive(SocketAsyncEventArgs e)
        {
            if(e.UserToken is ConnectionContext cc && 
               e.BytesTransferred >0 &&
               e.SocketError == SocketError.Success)
            {
                if(cc.State == (ConnectionContextOperation.Cache|
                    ConnectionContextOperation.ReceiveOp))
                {
                    if ( e.BytesTransferred != 4 )
                        return;
                    var length = new byte[e.BytesTransferred];
                    Array.Copy( e.Buffer, e.Offset, length, 0, e.BytesTransferred );
                    cc.ContentLength = BitConverter.ToInt32( length, 0 );
                    cc.State = ConnectionContextOperation.Transmit | ConnectionContextOperation.ReceiveOp;
                }
                else
                {
                    if ( cc.State != ( ConnectionContextOperation.ReceiveOp | ConnectionContextOperation.Transmit ) )
                        return;
                    var data = alloc.Pop( e );
                    if ( cc.ContentLength > 0 && cc.ContentLength >= data.Count)
                    {
                        cc.RecvBuffer.AddRange( data );
                        cc.ContentLength -= data.Count;
                    }
                    else
                    {
                        var d = new ArraySegment<byte>( data.Array, data.Offset, cc.ContentLength );
                        cc.ContentLength = 0;
                        cc.RecvBuffer.AddRange( d );
                    }

                    alloc.Push( data );
                    if(cc.ContentLength == 0 )
                    {
                        var d1 = cc.RecvBuffer.ToArray();
                        cc.RecvBuffer.Clear();
                        _OnReceive?.Invoke( cc, d1 );
                        cc.State = ConnectionContextOperation.Cache | ConnectionContextOperation.ReceiveOp;
                    }
                }
            }
            else
            {
                //关闭连接
                Disconnect( e );
            }
        }

        public event Action<ConnectionContext> _OnSend;
        public void Send(SocketAsyncEventArgs e)
        {
            if ( e.Buffer.Length == 4096 )
                e.SetBuffer( 0, 4096 );
            _OnSend?.Invoke( e.UserToken as ConnectionContext );
        }
    }
}
